/** @类 操作类 */
export class Toucher {
  constructor(renderer) {
    this.isDown = false; // 是否按下
    this.isZoom = false; // 是否缩放
    this.isDrag = false; // 是否拖拽
    this.downPoint = { x: 0, y: 0 }; // 按下坐标
    this.downOffset = { x: 0, y: 0 }; // 按下偏移量
    this.downDistance = 0; // 按下缩放距离
    this.renderer = renderer;
    const { canvas } = renderer;
    canvas.addEventListener('mousedown', this.onMousedown.bind(this));
    canvas.addEventListener('mousemove', this.onMousemove.bind(this));
    canvas.addEventListener('mouseup', this.onMouseup.bind(this));
    canvas.addEventListener('touchstart', this.onMousedown.bind(this));
    canvas.addEventListener('touchmove', this.onMousemove.bind(this));
    canvas.addEventListener('touchend', this.onMouseup.bind(this));
    canvas.addEventListener('wheel', this.onWheel.bind(this));
    document.addEventListener('mouseup', this.onMouseup.bind(this));
    document.addEventListener('touchend', this.onMouseup.bind(this));
  }

  /** @方法 销毁监听 */
  destroy() {
    const { canvas } = this.renderer;
    canvas.removeEventListener('mousedown', this.onMousedown.bind(this));
    canvas.removeEventListener('mousemove', this.onMousemove.bind(this));
    canvas.removeEventListener('mouseup', this.onMouseup.bind(this));
    canvas.removeEventListener('touchstart', this.onMousedown.bind(this));
    canvas.removeEventListener('touchmove', this.onMousemove.bind(this));
    canvas.removeEventListener('touchend', this.onMouseup.bind(this));
    canvas.removeEventListener('wheel', this.onWheel.bind(this));
    document.removeEventListener('mouseup', this.onMouseup.bind(this));
    document.removeEventListener('touchend', this.onMouseup.bind(this));
  }

  /** @事件 鼠标滚动 */
  onWheel(e) {
    this.Dispatch('zoom', { zoom: e.deltaY < 0 });
  }

  /** @事件 鼠标抬起 */
  onMouseup(e) {
    if (this.isDown) {
      e.stopPropagation();
      e.preventDefault();
      !this.isDrag && !this.isZoom && this.Dispatch('click', { point: this.downPoint });
    }

    // 移除状态
    this.isDown = false;
    this.isZoom = false;
    this.isDrag = false;
    this.downPoint = { x: 0, y: 0 };
    this.downOffset = { x: 0, y: 0 };
    this.downDistance = 0;
  }

  /** @事件 鼠标按下 */
  onMousedown(e) {
    const touches = this.GetTouchs(e);
    const { background } = this.renderer.parser;
    if (touches.length === 1) {
      this.downOffset = { x: background.offsetX, y: background.offsetY };
      this.downPoint = this.GetPos(e);
      this.isDown = true;
    } else if (touches.length === 2) {
      this.downDistance = this.GetDistance(touches);
      this.isZoom = true;
    }
  }

  /** @事件 鼠标移动 */
  onMousemove(e) {
    if (this.isZoom) {
      const newTouches = this.GetTouchs(e);
      const newDistance = this.GetDistance(newTouches);
      this.Dispatch('zoom', { zoom: newDistance > this.downDistance });
    } else if (this.isDown) {
      const newPoint = this.GetPos(e);
      (newPoint.x !== this.downPoint.x || newPoint.y !== this.downPoint.y) && (this.isDrag = true);
      this.isDrag && this.Dispatch('drag', {
        point: newPoint,
        downPoint: this.downPoint,
        downOffset: this.downOffset,
      });
    }
  }

  /** @辅助 触发事件 */
  Dispatch(type, ...args) {
    this.renderer.parser && this.renderer.observer.dispatch(type, ...args);
  }

  /** @辅助 获取移动端缩放距离 */
  GetDistance(touches) {
    return Math.sqrt(
      Math.pow(touches[0].x - touches[1].x, 2) +
        Math.pow(touches[0].y - touches[1].y, 2)
    );
  }

  /** @辅助 获取移动端手指 */
  GetTouchs(e) {
    // 获取基于画板的相对位置
    const { left, top } = this.renderer.canvas.getBoundingClientRect();
    // 获取两个手指的坐标
    return Array.from(e.touches ? e.touches : e.changedTouches ? e.changedTouches : [e]).map((touch) => {
      const { clientX, clientY } = touch;
      return {
        x: clientX - left,
        y: clientY - top,
      };
    });
  }

  /** @辅助 获取点击坐标 */
  GetPos(e) {
    // 获取鼠标位置
    const { clientX, clientY } = e.touches ? e.touches[0] : e.changedTouches ? e.changedTouches[0] : e;
    // 获取基于画板的相对位置
    const { left, top } = this.renderer.canvas.getBoundingClientRect();
    // 返回缩放后的相对位置
    return {
      x: clientX - left,
      y: clientY - top,
    };
  }
}

export default Toucher;
